{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/admin/anaconda3/lib/python3.6/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n",
      "  from ._conv import register_converters as _register_converters\n",
      "Using TensorFlow backend.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "./sample_data/labeled_black/dns2tcp_cmd.pcap.txt\n",
      "./sample_data/labeled_black/dns2tcp_sendfile9.pcap.txt\n",
      "./sample_data/labeled_black/dns2tcp_when_use_ssh.txt\n",
      "./sample_data/labeled_black/dnscapy_scp.pcap.txt\n",
      "./sample_data/labeled_black/dnscapy_scp2.pcap.txt\n",
      "./sample_data/labeled_black/dnscat2_when_exec_command_rm_file.txt\n",
      "./sample_data/labeled_black/dnscat2_when_idle.txt\n",
      "./sample_data/labeled_black/download_dnscat2_file12.pcap.txt\n",
      "./sample_data/labeled_black/download_dnscat2_file13.pcap.txt\n",
      "./sample_data/labeled_black/download_dnscat2_file14.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_scp17_base128.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_scp3_base64.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_ssh4_base32.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_ssh6_base128.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_ssh6_base64.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_ssh6_base64u.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_ssh9_base32_again.pcap.txt\n",
      "./sample_data/labeled_black/iodine_idle_direct_idle44.pcap.txt\n",
      "./sample_data/labeled_black/nbtoo_dnscat_file7.pcap.txt\n",
      "./sample_data/labeled_black/ozyman_idle.pcap.txt\n",
      "./sample_data/labeled_black/ozyman_idle2.pcap.txt\n",
      "./sample_data/labeled_black/ozyman_idle3.pcap.txt\n",
      "./sample_data/labeled_black/tcp-over-dns-idle.pcap.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-ctripgslb.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-dlgslb.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-gosuncdn.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-mccdnglb.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-mmycdn.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-ruisucdn.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-spcdntip.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-tcdnvod.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-8-tcdnvod.com.txt\n",
      "./sample_data/labeled_white/2017-8-15-0-henanpeace.org.cn.txt\n",
      "./sample_data/labeled_white/2017-8-15-8-qichedaquan.com.txt\n",
      "./sample_data/labeled_white/2017-8-16-11-lse.ac.uk.txt\n",
      "./sample_data/labeled_white/2017-8-16-9-dicp.ac.cn.txt\n",
      "./sample_data/labeled_white/2017-8-2-0-365yg.com.txt\n",
      "./sample_data/labeled_white/2017-8-2-0-bilibiligame.net.txt\n",
      "X len: 4439 Y len: 4439\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "embedding_1 (Embedding)      (None, 256, 64)           2752      \n",
      "_________________________________________________________________\n",
      "lstm_1 (LSTM)                (None, 16)                5184      \n",
      "_________________________________________________________________\n",
      "dense_1 (Dense)              (None, 1)                 17        \n",
      "=================================================================\n",
      "Total params: 7,953\n",
      "Trainable params: 7,953\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n"
     ]
    },
    {
     "ename": "ValueError",
     "evalue": "Error when checking model input: the list of Numpy arrays that you are passing to your model is not the size the model expected. Expected to see 1 array(s), but instead got the following list of 3551 arrays: [array([[28.],\n       [32.],\n       [18.],\n       [ 3.],\n       [38.],\n       [ 6.],\n       [16.],\n       [ 9.],\n       [34.],\n       [28.],\n       [38.],\n       [33.],\n       [41.],\n       [32.],\n   ...",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-1-0ca3d2a0aadc>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m    235\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    236\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0m__name__\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"__main__\"\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 237\u001b[0;31m     \u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-1-0ca3d2a0aadc>\u001b[0m in \u001b[0;36mrun\u001b[0;34m()\u001b[0m\n\u001b[1;32m    223\u001b[0m     \u001b[0mmodel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbuild_model_BiRNN\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmax_len\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvolcab_size\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    224\u001b[0m     \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msummary\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 225\u001b[0;31m     \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtrainX\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtrainY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalidation_data\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtestX\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtestY\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mbatch_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m32\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    226\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    227\u001b[0m     \u001b[0mfilename\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'finalized_model.tflearn'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/keras/engine/training.py\u001b[0m in \u001b[0;36mfit\u001b[0;34m(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, **kwargs)\u001b[0m\n\u001b[1;32m    948\u001b[0m             \u001b[0msample_weight\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msample_weight\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    949\u001b[0m             \u001b[0mclass_weight\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mclass_weight\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 950\u001b[0;31m             batch_size=batch_size)\n\u001b[0m\u001b[1;32m    951\u001b[0m         \u001b[0;31m# Prepare validation data.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    952\u001b[0m         \u001b[0mdo_validation\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/keras/engine/training.py\u001b[0m in \u001b[0;36m_standardize_user_data\u001b[0;34m(self, x, y, sample_weight, class_weight, check_array_lengths, batch_size)\u001b[0m\n\u001b[1;32m    747\u001b[0m             \u001b[0mfeed_input_shapes\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    748\u001b[0m             \u001b[0mcheck_batch_axis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m  \u001b[0;31m# Don't enforce the batch size.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 749\u001b[0;31m             exception_prefix='input')\n\u001b[0m\u001b[1;32m    750\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    751\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/keras/engine/training_utils.py\u001b[0m in \u001b[0;36mstandardize_input_data\u001b[0;34m(data, names, shapes, check_batch_axis, exception_prefix)\u001b[0m\n\u001b[1;32m     99\u001b[0m                 \u001b[0;34m'Expected to see '\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnames\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m' array(s), '\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    100\u001b[0m                 \u001b[0;34m'but instead got the following list of '\u001b[0m \u001b[0;34m+\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 101\u001b[0;31m                 str(len(data)) + ' arrays: ' + str(data)[:200] + '...')\n\u001b[0m\u001b[1;32m    102\u001b[0m         \u001b[0;32melif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnames\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    103\u001b[0m             raise ValueError(\n",
      "\u001b[0;31mValueError\u001b[0m: Error when checking model input: the list of Numpy arrays that you are passing to your model is not the size the model expected. Expected to see 1 array(s), but instead got the following list of 3551 arrays: [array([[28.],\n       [32.],\n       [18.],\n       [ 3.],\n       [38.],\n       [ 6.],\n       [16.],\n       [ 9.],\n       [34.],\n       [28.],\n       [38.],\n       [33.],\n       [41.],\n       [32.],\n   ..."
     ]
    }
   ],
   "source": [
    "# -*- coding: utf-8 -*-\n",
    "from __future__ import division, absolute_import\n",
    "\n",
    "import tldextract\n",
    "#import tflearn\n",
    "import os\n",
    "import pickle\n",
    "import numpy as np\n",
    "import keras\n",
    "#from tflearn.data_utils import to_categorical, pad_sequences\n",
    "#import tensorflow as tf\n",
    "#import tflearn\n",
    "#from tflearn.layers.core import input_data, dropout, fully_connected\n",
    "#from tflearn.layers.conv import conv_1d, global_max_pool\n",
    "#from tflearn.layers.merge_ops import merge\n",
    "#from tflearn.layers.estimator import regression\n",
    "#from tflearn.data_utils import to_categorical, pad_sequences\n",
    "from sklearn.model_selection import train_test_split\n",
    "import pickle\n",
    "from keras.utils import to_categorical\n",
    "from keras.models import Sequential\n",
    "from keras.layers import Dense, Embedding\n",
    "from keras.layers import LSTM,Bidirectional,GRU\n",
    "SRC_IP_IDX = 3-1               \n",
    "DST_IP_IDX = 4-1               \n",
    "SRC_PORT_IDX = 5-1             \n",
    "DST_PORT_IDX = 6-1             \n",
    "PROTOCOL_IDX = 7-1             \n",
    "DNS_QUERY_NAME_IDX = 55-1 # domain\n",
    "DNS_REQUEST_TYPE = 56-1\n",
    "DNS_DOMAIN_TTL = 59-1\n",
    "DNS_REPLY_IPV4IP = 60-1        \n",
    "DNS_REPLY_IPV6IP = 61-1        \n",
    "DNS_REPLY_RRTYPE = 62-1        \n",
    "DNS_REQUEST_LEN  = 88-1        \n",
    "DNS_REPLY_LENGTH = 90-1        \n",
    "  \n",
    "\n",
    "def iterbrowse(path):\n",
    "    for home, dirs, files in os.walk(path):\n",
    "        for filename in files:\n",
    "            yield os.path.join(home, filename)\n",
    "\n",
    "def pad_sequences(X, maxlen, value=0):\n",
    "    S=[]\n",
    "    for x in X:\n",
    "        xlen = len(x)\n",
    "        if xlen < maxlen:\n",
    "            x.extend([value]*(maxlen-xlen))\n",
    "        else:\n",
    "            x = x[:maxlen]\n",
    "        S.append(x)\n",
    "    return S\n",
    "def extract_domain(domain):\n",
    "    suffix = {'.com','.la','.io', '.co', '.cn','.info', '.net', '.org','.me', '.mobi', '.us', '.biz', '.xxx', '.ca', '.co.jp', '.com.cn', '.net.cn', '.org.cn', '.mx','.tv', '.ws', '.ag', '.com.ag', '.net.ag', '.org.ag','.am','.asia', '.at', '.be', '.com.br', '.net.br', '.name', '.live', '.news', '.bz', '.tech', '.pub', '.wang', '.space', '.top', '.xin', '.social', '.date', '.site', '.red', '.studio', '.link', '.online', '.help', '.kr', '.club', '.com.bz', '.net.bz', '.cc', '.band', '.market', '.com.co', '.net.co', '.nom.co', '.lawyer', '.de', '.es', '.com.es', '.nom.es', '.org.es', '.eu', '.wiki', '.design', '.software', '.fm', '.fr', '.gs', '.in', '.co.in', '.firm.in', '.gen.in', '.ind.in', '.net.in', '.org.in', '.it', '.jobs', '.jp', '.ms', '.com.mx', '.nl','.nu','.co.nz','.net.nz', '.org.nz', '.se', '.tc', '.tk', '.tw', '.com.tw', '.idv.tw', '.org.tw', '.hk', '.co.uk', '.me.uk', '.org.uk', '.vg'}\n",
    "\n",
    "    domain = domain.lower()\n",
    "    names = domain.split(\".\")\n",
    "    if len(names) >= 3:\n",
    "        if (\".\"+\".\".join(names[-2:])) in suffix:\n",
    "            return \".\".join(names[-3:]), \".\".join(names[:-3])\n",
    "        elif (\".\"+names[-1]) in suffix:\n",
    "            return \".\".join(names[-2:]), \".\".join(names[:-2])\n",
    "    #print (\"New domain suffix found. Use tld extract domain...\")\n",
    "\n",
    "    pos = domain.rfind(\"/\")\n",
    "    if pos >= 0: # maybe subdomain contains /, for dns tunnel tool\n",
    "        ext = tldextract.extract(domain[pos+1:])\n",
    "        subdomain = domain[:pos+1] + ext.subdomain\n",
    "    else:\n",
    "        ext = tldextract.extract(domain)\n",
    "        subdomain = ext.subdomain\n",
    "    if ext.suffix:\n",
    "        mdomain = ext.domain + \".\" + ext.suffix\n",
    "    else:\n",
    "        mdomain = ext.domain\n",
    "    return mdomain, subdomain\n",
    "\n",
    "\n",
    "def filter_metadata_dns(data):\n",
    "    if(len(data) < 91):\n",
    "        return False\n",
    "\n",
    "    protol  = data[PROTOCOL_IDX]\n",
    "    dstport = data[DST_PORT_IDX]\n",
    "    dstip   = data[DST_IP_IDX]\n",
    "    qname   = data[DNS_QUERY_NAME_IDX]\n",
    "\n",
    "    if '' == qname or '' == dstip:\n",
    "        return False\n",
    "    if '17' == protol and ('53' == dstport):\n",
    "        return True\n",
    "    return False\n",
    "\n",
    "\n",
    "def metadata2_domain_data(log): \n",
    "    data = log.split('^')\n",
    "    if not filter_metadata_dns(data):\n",
    "        return None, None\n",
    "    domain = data[DNS_QUERY_NAME_IDX]\n",
    "    mdomain, subdomain = extract_domain(domain)\n",
    "    return (mdomain, subdomain)\n",
    "\n",
    "\n",
    "def get_local_data(tag=\"labeled\"):\n",
    "    data_path = \"./sample_data\"\n",
    "    black_data, cdn_data, white_data = [], [], []    \n",
    "    for dir_name in (\"black\", \"cdn\", \"white\"):\n",
    "        dir_path = \"%s/%s_%s\" % (data_path, tag, dir_name)\n",
    "\n",
    "        for path in iterbrowse(dir_path):\n",
    "            print(path)\n",
    "            with open(path) as f:\n",
    "                for line in f:\n",
    "                    mdomain, subdomain = metadata2_domain_data(line)\n",
    "                    if subdomain is not None:\n",
    "                        if \"white\" in path:\n",
    "                            white_data.append(subdomain)\n",
    "                        elif \"cdn\" in path:\n",
    "                            cdn_data.append(subdomain)\n",
    "                        elif \"black\" in path and \"pcap\" in path:\n",
    "                            black_data.append(subdomain)\n",
    "                        else:\n",
    "                            pass\n",
    "                            #print (\"pass path:\", path)\n",
    "                    #else:\n",
    "                    #    print (\"unknown line:\", line, \" in file:\", path)\n",
    "    return black_data, cdn_data, white_data\n",
    "\n",
    "\n",
    "class LABEL(object):\n",
    "    white = 0\n",
    "    cdn = 1\n",
    "    black = 2\n",
    "\n",
    "\n",
    "def get_data():\n",
    "    black_x, cdn_x, white_x = get_local_data()\n",
    "    black_y, cdn_y, white_y = [LABEL.black]*len(black_x), [LABEL.cdn]*len(cdn_x), [LABEL.white]*len(white_x)\n",
    "\n",
    "    X = black_x + cdn_x + white_x\n",
    "    labels = black_y + cdn_y + white_y\n",
    "\n",
    "    # Generate a dictionary of valid characters\n",
    "    valid_chars = {x:idx+1 for idx, x in enumerate(set(''.join(X)))}\n",
    "\n",
    "    max_features = len(valid_chars) + 1\n",
    "    #print \"max_features:\", max_features\n",
    "    maxlen = np.max([len(x) for x in X])\n",
    "    #print \"max_len:\", maxlen\n",
    "    maxlen = min(maxlen, 256)\n",
    "\n",
    "    # Convert characters to int and pad\n",
    "    X = [[valid_chars[y] for y in x] for x in X]\n",
    "    X = pad_sequences(X, maxlen=maxlen, value=0.)\n",
    "\n",
    "    # Convert labels to 0-1\n",
    "    #print(labels)\n",
    "    Y = keras.utils.to_categorical(labels, num_classes=3)\n",
    "    \n",
    "    volcab_file = \"volcab.pkl\"\n",
    "    output = open(volcab_file, 'wb')\n",
    "    # Pickle dictionary using protocol 0.\n",
    "    data = {\"valid_chars\": valid_chars, \"max_len\": maxlen, \"volcab_size\": max_features}\n",
    "    pickle.dump(data, output)\n",
    "    output.close()\n",
    "\n",
    "    return X, Y, maxlen, max_features\n",
    "def build_model_BiRNN(max_len, volcab_size):\n",
    "    \"\"\"Build Bi-RNN model\"\"\"\n",
    "    model = Sequential()\n",
    "    model.add(Embedding(input_dim=volcab_size,\n",
    "                        output_dim=64,\n",
    "                        input_length=max_len))\n",
    "    model.add(LSTM(16, dropout=0.2, recurrent_dropout=0.2))\n",
    "    model.add(Dense(1, activation='sigmoid'))\n",
    "    #model.summary()\n",
    "    model.compile(loss='binary_crossentropy',\n",
    "                  optimizer='rmsprop',\n",
    "                  metrics=['accuracy'])\n",
    "    return model\n",
    "\n",
    "def get_rnn_model(max_len, volcab_size):\n",
    "    # Network building\n",
    "    net = tflearn.input_data([None, max_len])\n",
    "    net = tflearn.embedding(net, input_dim=volcab_size, output_dim=64)\n",
    "    net = tflearn.lstm(net, 64, dropout=0.8)\n",
    "    net = tflearn.fully_connected(net, 3, activation='softmax')\n",
    "    net = tflearn.regression(net, optimizer='adam', learning_rate=0.001,\n",
    "\t\t\t     loss='categorical_crossentropy')\n",
    "    model = tflearn.DNN(net, tensorboard_verbose=0)\n",
    "    return model\n",
    "\n",
    "\n",
    "def get_cnn_model(max_len, volcab_size):\n",
    "    # Building convolutional network\n",
    "    network = tflearn.input_data(shape=[None, max_len], name='input')\n",
    "    network = tflearn.embedding(network, input_dim=volcab_size, output_dim=64)\n",
    "    branch1 = conv_1d(network, 128, 3, padding='valid', activation='relu', regularizer=\"L2\")\n",
    "    branch2 = conv_1d(network, 128, 4, padding='valid', activation='relu', regularizer=\"L2\")\n",
    "    branch3 = conv_1d(network, 128, 5, padding='valid', activation='relu', regularizer=\"L2\")\n",
    "    network = merge([branch1, branch2, branch3], mode='concat', axis=1)\n",
    "    network = tf.expand_dims(network, 2)\n",
    "    network = global_max_pool(network)\n",
    "    network = dropout(network, 0.5)\n",
    "    network = fully_connected(network, 3, activation='softmax')\n",
    "    network = regression(network, optimizer='adam', learning_rate=0.001,\n",
    "                         loss='categorical_crossentropy', name='target')\n",
    "    model = tflearn.DNN(network, tensorboard_verbose=0)\n",
    "    return model\n",
    "\n",
    "\n",
    "def run():\n",
    "    X, Y, max_len, volcab_size = get_data()\n",
    "\n",
    "    print(\"X len:\", len(X), \"Y len:\", len(Y))\n",
    "    trainX, testX, trainY, testY = train_test_split(X, Y, test_size=0.2, random_state=42)\n",
    "    trainX=np.array(trainX)\n",
    "    testX=np.array(testX)\n",
    "    #print(trainX[:1])\n",
    "    #print(trainY[:1])\n",
    "    #print(testX[-1:])\n",
    "    #print(testY[-1:])\n",
    "    #print(type(trainX))\n",
    "    model = build_model_BiRNN(max_len, volcab_size)\n",
    "    model.summary()\n",
    "    model.fit(trainX, trainY, validation_data=(testX, testY),batch_size=32)\n",
    "   \n",
    "    filename = 'finalized_model.tflearn'\n",
    "    model.save(filename)\n",
    "\n",
    "    model.load(filename)\n",
    "    #print \"Just review 3 sample data test result:\"\n",
    "    result = model.predict(testX[0:3])\n",
    "    print(result)\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    run()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "./sample_data/labeled_black/dns2tcp_cmd.pcap.txt\n",
      "./sample_data/labeled_black/dns2tcp_sendfile9.pcap.txt\n",
      "./sample_data/labeled_black/dns2tcp_when_use_ssh.txt\n",
      "./sample_data/labeled_black/dnscapy_scp.pcap.txt\n",
      "./sample_data/labeled_black/dnscapy_scp2.pcap.txt\n",
      "./sample_data/labeled_black/dnscat2_when_exec_command_rm_file.txt\n",
      "./sample_data/labeled_black/dnscat2_when_idle.txt\n",
      "./sample_data/labeled_black/download_dnscat2_file12.pcap.txt\n",
      "./sample_data/labeled_black/download_dnscat2_file13.pcap.txt\n",
      "./sample_data/labeled_black/download_dnscat2_file14.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_scp17_base128.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_scp3_base64.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_ssh4_base32.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_ssh6_base128.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_ssh6_base64.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_ssh6_base64u.pcap.txt\n",
      "./sample_data/labeled_black/iodine_direct_ssh9_base32_again.pcap.txt\n",
      "./sample_data/labeled_black/iodine_idle_direct_idle44.pcap.txt\n",
      "./sample_data/labeled_black/nbtoo_dnscat_file7.pcap.txt\n",
      "./sample_data/labeled_black/ozyman_idle.pcap.txt\n",
      "./sample_data/labeled_black/ozyman_idle2.pcap.txt\n",
      "./sample_data/labeled_black/ozyman_idle3.pcap.txt\n",
      "./sample_data/labeled_black/tcp-over-dns-idle.pcap.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-ctripgslb.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-dlgslb.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-gosuncdn.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-mccdnglb.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-mmycdn.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-ruisucdn.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-spcdntip.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-0-tcdnvod.com.txt\n",
      "./sample_data/labeled_cdn/2017-8-2-8-tcdnvod.com.txt\n",
      "./sample_data/labeled_white/2017-8-15-0-henanpeace.org.cn.txt\n",
      "./sample_data/labeled_white/2017-8-15-8-qichedaquan.com.txt\n",
      "./sample_data/labeled_white/2017-8-16-11-lse.ac.uk.txt\n",
      "./sample_data/labeled_white/2017-8-16-9-dicp.ac.cn.txt\n",
      "./sample_data/labeled_white/2017-8-2-0-365yg.com.txt\n",
      "./sample_data/labeled_white/2017-8-2-0-bilibiligame.net.txt\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[5,\n",
       " 5,\n",
       " 5,\n",
       " 5,\n",
       " 5,\n",
       " 8,\n",
       " 1,\n",
       " 30,\n",
       " 29,\n",
       " 5,\n",
       " 38,\n",
       " 22,\n",
       " 5,\n",
       " 42,\n",
       " 33,\n",
       " 12,\n",
       " 38,\n",
       " 5,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0,\n",
       " 0.0]"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X, Y, max_len, volcab_size = get_data()\n",
    "X[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(4439, 256)\n",
      "(4439, 3)\n"
     ]
    }
   ],
   "source": [
    "print(np.shape(X))\n",
    "print(np.shape(Y))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainX, testX, trainY, testY = train_test_split(X, Y, test_size=0.2, random_state=42)\n",
    "#print(type(trainX[0][0]))\n",
    "#print(len(trainX))\n",
    "#print(len(trainY))\n",
    "#print(type(trainY))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3551, 256)\n",
      "(3551, 3)\n"
     ]
    }
   ],
   "source": [
    "print(np.shape(trainX))\n",
    "print(np.shape(trainY))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3551, 256)\n",
      "(3551, 3)\n"
     ]
    }
   ],
   "source": [
    "trainX=np.array(trainX)\n",
    "#print((trainX[0:2]))\n",
    "#print(type(trainX))\n",
    "#print(type(trainX[0:2]))\n",
    "testX=np.array(testX)\n",
    "print(np.shape(trainX))\n",
    "trainY=np.array(trainY)\n",
    "#trainY=trainY.reshape(trainY.shape[0],1)\n",
    "#testY=np.array(testY)\n",
    "#testY=testY.reshape(testY.shape[0],1)\n",
    "#print((trainY))\n",
    "print(np.shape(trainY))\n",
    "#trainY=np.mat(trainY).flatten().T\n",
    "#testY=np.mat(testY).flatten().T\n",
    "#print(np.shape(trainY))\n",
    "#print(trainY)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'numpy.ndarray'>\n",
      "<class 'numpy.ndarray'>\n"
     ]
    }
   ],
   "source": [
    "print(type(trainX))\n",
    "print(type(trainY))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "from keras.layers import LSTM,Bidirectional,GRU\n",
    "def build_model_BRNN(max_len, volcab_size):\n",
    "    \"\"\"Build Bi-RNN model\"\"\"\n",
    "    model = Sequential()\n",
    "    model.add(Embedding(input_dim=volcab_size,\n",
    "                        output_dim=64,\n",
    "                        input_length=max_len))\n",
    "    model.add(Bidirectional(GRU(16)))          \n",
    "    #model.add(Dense(16, activation='relu'))\n",
    "    model.add(Dense(3, activation='softmax'))\n",
    "    #model.summary()\n",
    "    model.compile(loss='categorical_crossentropy',\n",
    "                  optimizer='rmsprop',\n",
    "                  metrics=['accuracy'])\n",
    "    return model\n",
    "model = build_model_BRNN(max_len, volcab_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "embedding_8 (Embedding)      (None, 256, 64)           2752      \n",
      "_________________________________________________________________\n",
      "bidirectional_2 (Bidirection (None, 32)                7776      \n",
      "_________________________________________________________________\n",
      "dense_8 (Dense)              (None, 3)                 99        \n",
      "=================================================================\n",
      "Total params: 10,627\n",
      "Trainable params: 10,627\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 3551 samples, validate on 888 samples\n",
      "Epoch 1/2\n",
      "3551/3551 [==============================] - 58s 16ms/step - loss: 0.7219 - acc: 0.7277 - val_loss: 0.4265 - val_acc: 0.8514\n",
      "Epoch 2/2\n",
      "3551/3551 [==============================] - 55s 16ms/step - loss: 0.3015 - acc: 0.8868 - val_loss: 0.1835 - val_acc: 0.9459\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<keras.callbacks.History at 0x7f7b64de7518>"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.fit(trainX, trainY,batch_size=32,epochs=2,validation_data=(testX, testY))\n",
    "#model.fit(trainX, trainY, validation_set=(testX, testY), show_metric=True, batch_size=32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 7. 41. 23. 35.  3. 38.  6. 19. 18. 19. 42.  8.  1. 11. 38.  8. 19. 30.\n",
      "  38.  6. 33.  5. 18. 17.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.]\n",
      " [ 9.  9.  9.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.]\n",
      " [14. 30. 18.  3. 32. 38.  6. 19. 18. 19. 42.  8.  1. 11. 38.  8. 19. 30.\n",
      "  38.  6. 33.  5. 18. 17.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.\n",
      "   0.  0.  0.  0.]]\n",
      "[[7.0289121e-04 6.3663411e-01 3.6266294e-01]\n",
      " [9.6811968e-01 1.3670329e-02 1.8209966e-02]\n",
      " [2.7849716e-03 9.9564326e-01 1.5718674e-03]]\n"
     ]
    }
   ],
   "source": [
    "print(testX[0:3])\n",
    "result = model.predict(testX[0:3])\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
